package com.sheng.project.service.impl;

import cn.hutool.core.util.StrUtil;
import com.sheng.project.service.WeChatService;
import com.sheng.project.wechat.cache.WeChatTokenCache;
import com.sheng.project.wechat.dto.AccessTokenResponse;
import com.sheng.project.wechat.dto.TicketResponse;
import com.sheng.project.wechat.feign.WeChatFeignClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * @author by ls
 * @date 2023/8/11
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WeChatServiceImpl implements WeChatService {

    private final String GRANT_TYPE = "client_credential";

    private final String TICKET_TYPE = "jsapi";

    private final WeChatFeignClient feignClient;

    @Value("${application.wx.appid:wxb9f2ff0d4af88b1e}")
    private String appid;

    @Value("${application.wx.app-secret:02e881359ecaa7ecc94fecca2c84c6d2}")
    private String secret;

    @Override
    public Map<String, String> jsapi(String url) {
        String ticket = getTicket();
        return sign(ticket, url);
    }

    /**
     * 获取 accessToken
     *
     * @return accessToken
     */
    public String getAccessToken() {
        Optional<String> optionalAccessToken = Optional.ofNullable(WeChatTokenCache.get("access_token"));
        return optionalAccessToken.orElseGet(() -> {
            // 注：需要在微信开发者平台放开当前 ip 的限制
            AccessTokenResponse response = feignClient.getToken(GRANT_TYPE, appid, secret);
            Assert.isTrue(!StrUtil.isEmpty(response.getAccess_token()), "accessToken 获取失败，errmsg：" + response.getErrmsg());
            WeChatTokenCache.set("access_token", response.getAccess_token());
            return response.getAccess_token();
        });
    }

    /**
     * 获取 ticket
     *
     * @return ticket
     */
    public String getTicket() {
        Optional<String> optionalTicket = Optional.ofNullable(WeChatTokenCache.get("ticket"));
        return optionalTicket.orElseGet(() -> {
            TicketResponse response = feignClient.getTicket(getAccessToken(), TICKET_TYPE);
            Assert.isTrue(!StrUtil.isEmpty(response.getTicket()), "ticket 获取失败，errmsg：" + response.getErrmsg());
            WeChatTokenCache.set("ticket", response.getTicket());
            return response.getTicket();
        });
    }

    /**
     * 获取 signature 签名
     *
     * @param ticket 微信的 ticket
     * @param url    要使用 jsapi 功能页面的全路径（不包含 # 以及之后的部分）
     * @return Map<String, String>
     */
    public Map<String, String> sign(String ticket, String url) {
        Map<String, String> ret = new HashMap<>(8);
        String nonceStr = createNonceStr();
        String timestamp = createTimestamp();
        String string1;
        String signature = "";

        string1 = "jsapi_ticket=" + ticket + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + url;
        log.info("获取的 string1 为：{}", string1);

        try {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(string1.getBytes(StandardCharsets.UTF_8));
            // 可通过网站进行验证加密是否正确 https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign
            signature = byteToHex(crypt.digest());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        ret.put("nonceStr", nonceStr);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);
        ret.put("appid", appid);
        return ret;
    }

    /**
     * byte 数组转化为字符串
     *
     * @param hash 要转化的数组
     * @return 字符串
     */
    private String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    /**
     * 获取随机字符串
     *
     * @return 随机字符串
     */
    private String createNonceStr() {
        return UUID.randomUUID().toString();
    }

    /**
     * 获取当前时间戳
     *
     * @return 当前时间戳字符串
     */
    private String createTimestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }
}
